//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//


#ifndef CPP_LIB_UNITS_H
#define CPP_LIB_UNITS_H

#include "cpp-lib/math-util.h"


namespace cpl {

namespace units {

//
// Values of some exotic units in SI.
//
// Sources:
//
// - http://www.chemie.fu-berlin.de/chemistry/general/units_en.html
// - http://www.sengpielaudio.com/Rechner-druckeinh.htm
// - http://www.wbuf.noaa.gov/tempfc.htm
// - http://www.wikipedia.org/
// - http://www.airbp.com/usga/index2b438.html?page=products&sub=avgas
//

inline double const slug () { return 14.5939    ; } // kg
inline double const pound() { return  0.4535924 ; } // kg

// Length units [m].
inline double const foot         () { return     .3048 ; }
inline double const inch         () { return     .0254 ; }
inline double const nautical_mile() { return 1852.     ; }
inline double const statute_mile () { return 1609.344  ; }
inline double const kilometer    () { return 1000.     ; }

// Volume units [m^3].
inline double const us_gallon() { return 3.785412e-3 ; }

// Time [second].
inline double const hour()   { return 3600. ; }
inline double const minute() { return 60.   ; } 

// Angle [radians].
inline double const degree() { return cpl::math::pi / 180. ; }

// Velocity in [meter/second].
inline double const knot                ()               
{ return nautical_mile() / hour  () ; }
inline double const mile_per_hour       ()      
{ return statute_mile () / hour  () ; }
inline double const kilometer_per_hour  () 
{ return kilometer    () / hour  () ; } 
inline double const foot_per_minute     ()    
{ return foot         () / minute() ; } 

// Angular velocity [radians/second].
inline double const rotation()
{ return 2 * cpl::math::pi ; }

inline double const rotation_per_minute()
{ return rotation() / minute() ; }


// Temperature [K].
// Zero degrees Celsius.
inline double const zero_celsius   () { return 273.15                     ; }
// Zero degrees Fahrenheit.
inline double const zero_fahrenheit() { return 255.3722222222222222222222 ; }

inline double const fahrenheit() { return .555555555555555555555555555555 ; }


// Weight force of a mass of one pound at average earth gravitation.
inline double const lbf() { return 4.448222 ; } // N

// Pressure [N/m^2].
// @ 32 deg Fahrenheit, according to
// http://www.sengpielaudio.com/Rechner-druckeinh.htm
inline double const inch_hg() { return 3386.388706 ; }

inline double const pounds_per_square_inch()
{ return lbf() / cpl::math::power< 2 >( inch() ) ; }

inline double const european_horse_power() { return 735.4987 ; } // Watt

// According to Bart Oolbekkink, American horses are stronger than their
// Eurpoean brothers.

inline double const american_horse_power() { return 745.7    ; } // Watt

// Derived units...
inline double us_gallon_per_hour() { return us_gallon() / hour() ; }


// The following aren't really units, but well...  

// The somehow ``average'' gravitation on our earth.
inline double gravitation() { return 9.80665 ; } // m/s^2

// Also not really...  The earth's ``average'' radius according to the
// IUGG [m].
inline double earth_radius() { return 6378137 ; }

// The density of Avgas 100LL fuel at some ``standard'' temperature [kg/m^3].
inline double density_avgas() { return 700 ; }
// The density of Jet A-1 fuel at some ``standard'' temperature [kg/m^3].
inline double density_jet_a1() { return 800 ; }


// The mass of a US gallon of Avgas 100LL at some ``standard''
// temperature [kg].
inline double us_gallon_avgas() { return us_gallon() * density_avgas() ; }

// The mass of a US gallon of Jet A-1 at some ``standard''
// temperature [kg].
inline double us_gallon_jet_a1() { return us_gallon() * density_jet_a1() ; }

// US gallon Avgas per hour [kg/s].
inline double us_gallon_avgas_per_hour()
{ return us_gallon_avgas() / hour() ; }

// US gallon Jet A-1 per hour [kg/s].
inline double us_gallon_jet_a1_per_hour()
{ return us_gallon_jet_a1() / hour() ; }

template< int kg = 0 , int m = 0 , int s = 0 , int K = 0 , typename T = double >
struct physical_quantity {

  explicit physical_quantity( T const& value ) : value( value ) {}
  operator T() const { return value ; } 

private:

  T value ; 

} ;



template< int kg , int m , int s , int K , typename T >
physical_quantity< kg , m , s , K , T >
operator+( physical_quantity< kg , m , s , K , T > const& a ,
           physical_quantity< kg , m , s , K , T > const& b
	 )
{ return a + b ; }


template< int kg1 , int m1 , int s1 , int K1 , 
          int kg2 , int m2 , int s2 , int K2 , 
	  typename T
	>
physical_quantity< kg1 + kg2 , m1 + m2 , s1 + s2 , K1 + K2 >
operator*( 
  physical_quantity< kg1 , m1 , s1 , K1 , T > const& a ,
  physical_quantity< kg2 , m2 , s2 , K2 , T > const& b
)
{ return a * b ; }


typedef physical_quantity< 0 , 1 , -1 , 0 , double > velocity ;
typedef physical_quantity< 0 , 1 ,  0 , 0 , double > length   ;
typedef physical_quantity< 0 , 0 ,  1 , 0 , double > time     ;

// time t ; speed v ;
// t * v ; // length

} // namespace units

} // namespace cpl


#endif // CPP_LIB_UNITS_H
